package com.example.demo.service.exam.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.alibaba.excel.write.style.row.SimpleRowHeightStyleStrategy;
import com.example.demo.base.config.auth.UserInfoShareHolder;
import com.example.demo.domain.entity.User;
import com.example.demo.domain.entity.VojObjectiveQuestion;
import com.example.demo.domain.exam.Exam;
import com.example.demo.domain.exam.ExamClass;
import com.example.demo.domain.exam.ExamClassRelation;
import com.example.demo.domain.exam.ExamPaper;
import com.example.demo.domain.exam.ExamUserAbnormalBehavior;
import com.example.demo.domain.exam.ExamUserScore;
import com.example.demo.domain.exam.ExamSubmitLog;
import com.example.demo.domain.exam.FilePath;
import com.example.demo.domain.exam.Paging;
import com.example.demo.domain.exam.UserExamAns;
import com.example.demo.dto.ExamUserScoreDTO;
import com.example.demo.dto.ProgrammingQuestionDTO;
import com.example.demo.enums.BehaviorType;
import com.example.demo.enums.PaperType;
import com.example.demo.exception.ExceptionUtils;
import com.example.demo.export.ColumnWidthStyleStrategy;
import com.example.demo.service.NewProblemService;
import com.example.demo.service.VojObjectiveQuestionService;
import com.example.demo.service.exam.ExamClassRelationService;
import com.example.demo.service.exam.ExamClassService;
import com.example.demo.service.exam.ExamPaperService;
import com.example.demo.service.exam.ExamService;
import com.example.demo.service.exam.ExamSituationService;
import com.example.demo.service.exam.ExamUserAbnormalBehaviorService;
import com.example.demo.service.exam.ExamUserScoreService;
import com.example.demo.service.exam.ExamSubmitLogService;
import com.example.demo.service.exam.ExamUserSingleQuesScoreService;
import com.example.demo.service.exam.FilePathService;
import com.example.demo.service.exam.UserExamAnswerService;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.springframework.stereotype.Service;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class ExamSituationServiceImpl implements ExamSituationService {

    private final List<List<String>> submitLogHeaderList = new ArrayList<>(Arrays.asList(
            Collections.singletonList("序号"),
            Collections.singletonList("学生id"),
            Collections.singletonList("学生姓名"),
            Collections.singletonList("提交题目"),
            Collections.singletonList("提交时间"),
            Collections.singletonList("得分"),
            Collections.singletonList("时间差值(分)")
    ));

    private final List<List<String>> scoreHeaderList = new ArrayList<>(Arrays.asList(
            Collections.singletonList("序号"),
            Collections.singletonList("考生号"),
            Collections.singletonList("姓名"),
            Collections.singletonList("成绩"),
            Collections.singletonList("所属班级"),
            Collections.singletonList("登录IP"),
            Collections.singletonList("进入时间"),
            Collections.singletonList("提交时间"),
            Collections.singletonList("用时(分)"),
            Collections.singletonList("状态")
    ));

    private final UserExamAnswerService userExamAnswerService;

    private final ExamPaperService examPaperService;

    private final ExamSubmitLogService submitLogService;

    private final ExamUserScoreService examUserScoreService;

    private final ExamUserSingleQuesScoreService examUserSingleQuesScoreService;

    private final FilePathService filePathService;

    private final ExamUserAbnormalBehaviorService examUserAbnormalBehaviorService;

    private final ExamService examService;

    private final ExamClassRelationService examClassRelationService;

    private final ExamClassService examClassService;

    private final VojObjectiveQuestionService vojObjectiveQuestionService;

    private final NewProblemService newProblemService;

    @Override
    public List<Exam> getRecentExam(Long num) {
        if (num <= 0) {
            return Collections.emptyList();
        }
        return examService.getRecentExam(new Paging(1L, num));
    }

    @Override
    public ExamPaper getExamPaper(Long paperId, String paperType) {
        System.out.println("paperType: "+ paperType);
        if (paperType.equals("ALL_QUESTIONS")){
            return examPaperService.getExamPaperDetail(paperId, PaperType.ALL_QUESTIONS);
        }else if (paperType.equals("ONLY_PROGRAMMING_QUESTIONS")){
            return examPaperService.getExamPaperDetail(paperId, PaperType.ONLY_PROGRAMMING_QUESTIONS);
        }else {
            return examPaperService.getExamPaperDetail(paperId, PaperType.ONLY_OBJECTIVE_QUESTIONS);
        }
    }

    @Override
    public void exportSubmitLog(Long examId, HttpServletResponse response) {
        List<ExamSubmitLog> submitLogs = submitLogService.getSubmitLogByExamId(examId);
        ExamSubmitLog submitLog = submitLogs.stream().findFirst().orElse(new ExamSubmitLog());
        ExcelWriter excelWriter = null;
        Map<Long, Timestamp> map = new HashMap<>();
        try (ServletOutputStream outputStream = response.getOutputStream()) {
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            String fileName = URLEncoder.encode(submitLog.getExamName() + ".xlsx", "UTF-8");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName);
            AtomicInteger i = new AtomicInteger(1);
            List<List<String>> dataList = submitLogs.stream().map(e -> {
                List<String> row = new ArrayList<>();
                row.add(String.valueOf(i.get()));
                row.add(String.valueOf(e.getUserId()));
                row.add(e.getUsername());
                row.add(String.valueOf(e.getQuestionId()));
                SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                row.add(format.format(e.getSubmitTime()));
                row.add(String.valueOf(e.getScore()));
                if (map.containsKey(e.getUserId())) {
                    Timestamp firstTime = map.get(e.getUserId());
                    long differenceMinutes = (e.getSubmitTime().getTime() - firstTime.getTime()) / (60 * 1000);
                    row.add(String.valueOf(differenceMinutes));
                } else {
                    map.putIfAbsent(e.getUserId(), e.getSubmitTime());
                    row.add(format.format(e.getSubmitTime()));
                }

                i.getAndIncrement();
                return row;
            }).collect(Collectors.toList());
            excelWriter = new ExcelWriter(outputStream, ExcelTypeEnum.XLSX, true);
            WriteSheet writeSheet = EasyExcel.writerSheet(0, fileName)
                    .head(submitLogHeaderList)
                    .registerWriteHandler(new SimpleRowHeightStyleStrategy((short) 23, (short) 20))
                    .registerWriteHandler(this.getCellStyleStrategy())
                    .registerWriteHandler(new ColumnWidthStyleStrategy())
                    .build();
            excelWriter.write(dataList, writeSheet);
            excelWriter.finish();
            outputStream.flush();
        } catch (Exception e) {
            throw ExceptionUtils.exception("导出文件失败");
        } finally {
            if (null != excelWriter) {
                excelWriter.finish();
            }
        }
    }

    @Override
    public void exportScore(Long examId, HttpServletResponse response) {
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        List<ExamUserScore> byExamId = examUserScoreService.getByExamId(examId);
        ExamUserScore examUserScore = byExamId.stream().findFirst().orElse(new ExamUserScore());
        ExcelWriter excelWriter = null;
        try (ServletOutputStream outputStream = response.getOutputStream()) {
            String fileName = URLEncoder.encode(examUserScore.getExamName() + ".xlsx", "UTF-8");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName);
            AtomicInteger i = new AtomicInteger(1);
            List<List<String>> dataList = byExamId.stream().map(e -> {
                List<String> row = new ArrayList<>();
                row.add(String.valueOf(i.get()));
                row.add(String.valueOf(e.getUserId()));
                row.add(e.getUsername());
                row.add(e.getScore() == null ? "" : String.valueOf(e.getScore()));
                row.add(StringUtils.isBlank(e.getClassName()) ? "" : e.getClassName());
                row.add(StringUtils.isBlank(e.getIp()) ? "" : e.getIp());
                SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                row.add(e.getEnterTime() == null ? "" : format.format(e.getEnterTime()));
                row.add(e.getSubmitTime() == null ? "" : format.format(e.getSubmitTime()));
                row.add((e.getSubmitTime() == null || e.getEnterTime() == null) ? "" : String.valueOf(((double) e.getSubmitTime().getTime() - e.getEnterTime().getTime()) / 60 / 1000));
                row.add(e.getStatus().getText());
                i.getAndIncrement();
                return row;
            }).collect(Collectors.toList());
            excelWriter = new ExcelWriter(outputStream, ExcelTypeEnum.XLSX, true);
            WriteSheet writeSheet = EasyExcel.writerSheet(0, fileName)
                    .head(scoreHeaderList)
                    .registerWriteHandler(new SimpleRowHeightStyleStrategy((short) 23, (short) 20))
                    .registerWriteHandler(this.getCellStyleStrategy())
                    .registerWriteHandler(new ColumnWidthStyleStrategy())
                    .build();
            excelWriter.write(dataList, writeSheet);
            excelWriter.finish();
            outputStream.flush();
        } catch (Exception e) {
            throw ExceptionUtils.exception("导出文件失败");
        } finally {
            if (null != excelWriter) {
                excelWriter.finish();
            }
        }
    }

    // @Override
    // public ExamUserScoreDTO getExamUserScore(Long examId, Long userId) {
    //     ExamUserScoreDTO dto = new ExamUserScoreDTO();
    //     ExamUserScore examUserScore = examUserScoreService.getByExamUserId(examId, userId);
    //     BeanUtil.copyProperties(examUserScore, dto);
    //     List<UserExamAns> answerList = userExamAnswerService.getExamAns(examId, userId);
    //     Map<Long, ExamUserSingleQuesScore> map = examUserSingleQuesScoreService.getByUserExamId(userId, examId).stream().collect(Collectors.toMap(ExamUserSingleQuesScore::getQuestionId, e -> e, (v1, v2) -> v1));
    //     dto.setAnswerList(answerList);
    //     answerList.forEach(e -> e.setSingleQuesScore(map.get(e.getQuestionId())));
    //     List<FilePath> byExamUserId = filePathService.getByExamUserId(examId, userId);
    //     Map<BehaviorType, List<ExamUserAbnormalBehavior>> typeListMap = examUserAbnormalBehaviorService.getByExamUserId(examId, userId).stream().collect(Collectors.groupingBy(ExamUserAbnormalBehavior::getType));
    //     dto.setTypeListMap(typeListMap);
    //     dto.setFilePathList(byExamUserId);
    //     return dto;
    // }
    @Override
    public ExamUserScoreDTO getExamUserScore(Long examId, Long userId) {
        ExamUserScoreDTO dto = new ExamUserScoreDTO();
        ExamUserScore examUserScore = examUserScoreService.getByExamUserId(examId, userId);
        BeanUtil.copyProperties(examUserScore, dto);
        List<UserExamAns> answerList = userExamAnswerService.getExamAns(examId, userId);
        dto.setAnswerList(answerList);
        List<FilePath> byExamUserId = filePathService.getByExamUserId(examId, userId);
        Map<BehaviorType, List<ExamUserAbnormalBehavior>> typeListMap = examUserAbnormalBehaviorService.getByExamUserId(examId, userId).stream().collect(Collectors.groupingBy(ExamUserAbnormalBehavior::getType));
        dto.setTypeListMap(typeListMap);
        dto.setFilePathList(byExamUserId);
        return dto;
    }

    @Override
    public Map<String, Object> getStatistics() {
        Map<String, Object> map = new HashMap<>();
        Long userId = UserInfoShareHolder.getUserInfo().getUser().getId();
        Long companyId = UserInfoShareHolder.getUserInfo().getCompanyId();
        List<Long> classIds = examClassService.getByCreatorId(userId, companyId).stream().map(ExamClass::getId).collect(Collectors.toList());
        List<ExamClassRelation> byClassIds = examClassRelationService.getByClassIds(classIds);
        List<ExamPaper> paperList = examPaperService.getByCreatorId(userId, companyId);
        List<VojObjectiveQuestion> vojObjectiveQuestionList = vojObjectiveQuestionService.getByCreatorId(userId, companyId);
        List<ProgrammingQuestionDTO> programmingQuestionDTOList = newProblemService.getByCreatorId(userId, companyId);

        map.put("classNum", classIds.size());
        map.put("userNum", byClassIds.size());
        map.put("paperNum", paperList.size());
        map.put("ObjectQuestionNum", vojObjectiveQuestionList.size());
        map.put("ProgrammingQuestionNum", programmingQuestionDTOList.size());
        return map;
    }

    private HorizontalCellStyleStrategy getCellStyleStrategy() {
        // 表头的策略
        WriteCellStyle headWriteCellStyle = getHeadCellStyle();
        // 内容策略
        WriteCellStyle contentWriteCellStyle = getColumnCellStyle();
        // 样式策略为表头策略加内容策略
        return new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
    }

    private WriteCellStyle getHeadCellStyle() {
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        WriteFont headWriteFont = new WriteFont();
        // 设置不加粗、字体、字号
        headWriteFont.setBold(false);
        headWriteFont.setFontName("黑体");
        headWriteFont.setFontHeightInPoints((short) 14);
        headWriteCellStyle.setWriteFont(headWriteFont);
        //设置左右居中
        headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        //设置边框
        headWriteCellStyle.setBorderBottom(BorderStyle.THIN);
        headWriteCellStyle.setBorderLeft(BorderStyle.THIN);
        headWriteCellStyle.setBorderRight(BorderStyle.THIN);
        headWriteCellStyle.setBorderTop(BorderStyle.NONE);
        //设置自动换行
        headWriteCellStyle.setWrapped(false);
        return headWriteCellStyle;
    }

    private static WriteCellStyle getColumnCellStyle() {
        // 表格内容的策略
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        // 设置边框
        contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);
        contentWriteCellStyle.setBorderLeft(BorderStyle.THIN);
        contentWriteCellStyle.setBorderRight(BorderStyle.THIN);
        contentWriteCellStyle.setBorderTop(BorderStyle.NONE);
        //设置字体、字号、不加粗
        WriteFont contentWriteFont = new WriteFont();
        contentWriteFont.setFontName("宋体");
        contentWriteFont.setFontHeightInPoints((short) 14);
        contentWriteFont.setBold(false);
        contentWriteCellStyle.setWriteFont(contentWriteFont);
        // 设置文字左右、上下居中
        contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        return contentWriteCellStyle;
    }

}
